/**
 * 
 */
package prefix.util;

import java.text.ParseException;
import java.text.SimpleDateFormat;
import java.util.Calendar;
import java.util.Date;
import java.util.GregorianCalendar;

import prefix.platform.Constants;



public class DateUtil implements Timeable {

    private static final String FORMAT_ONE = "yyyy-MM-dd HH:mm:ss";

    // -------------------------------------------------------------------------------------------
    public static int getDayOfYear() {
        GregorianCalendar cal = new GregorianCalendar();
        cal.setTime(new Date());

        return cal.get(Calendar.DAY_OF_YEAR);
    }

    // -------------------------------------------------------------------------------------------
    public static Date addGracePeriod(Date date) {
        GregorianCalendar cal = new GregorianCalendar();
        cal.setTime(date);
        cal.add(Calendar.DAY_OF_MONTH, Constants.LOAN_APP_DURATION_IN_DAYS);
        cal.add(Calendar.MONTH, 1);
        Date dueDate = cal.getTime();

        return dueDate;
    }

    // -------------------------------------------------------------------------------------------
    // Assumes numberOfDays <= 7
    public static Date addBusiessDays(Date baseDate, int numberOfDays) {
        if (baseDate == null)
            return null;
        GregorianCalendar cal = new GregorianCalendar();
        cal.setTime(baseDate);

        int i = 0;
        while (i < numberOfDays) {
            cal.add(Calendar.DAY_OF_WEEK, 1);
            if (!isExcludedDate(cal.getTime()))
                i++;
        }
        return cal.getTime();
    }

    // -------------------------------------------------------------------------------------------
    // Assumes numberOfDays <= 7
    public static Date deductBusiessDays(Date baseDate, int numberOfDays) {
        if (baseDate == null)
            return null;
        GregorianCalendar cal = new GregorianCalendar();
        cal.setTime(baseDate);

        int i = 0;
        while (i < numberOfDays) {
            cal.add(Calendar.DAY_OF_WEEK, -1);
            if (!isExcludedDate(cal.getTime()))
                i++;
        }
        return cal.getTime();
    }

    // -------------------------------------------------------------------------------------------
    public static Date addRegularDays(Date baseDate, int numberOfDays) {
        if (baseDate == null)
            return null;
        GregorianCalendar cal = new GregorianCalendar();
        cal.setTime(baseDate);

        cal.add(Calendar.DAY_OF_YEAR, numberOfDays);
        return cal.getTime();
    }

    public static Date addHours(Date baseDate, int numberOfHours) {
        if (baseDate == null)
            return null;
        GregorianCalendar cal = new GregorianCalendar();
        cal.setTime(baseDate);

        cal.add(Calendar.HOUR_OF_DAY, numberOfHours);
        return cal.getTime();
    }

    public static Date addMins(Date baseDate, int numberOfMins) {
        if (baseDate == null)
            return null;
        GregorianCalendar cal = new GregorianCalendar();
        cal.setTime(baseDate);

        cal.add(Calendar.MINUTE, numberOfMins);
        return cal.getTime();
    }

    public static Date addSeconds(Date baseDate, int numberOfSec) {
        if (baseDate == null)
            return null;
        GregorianCalendar cal = new GregorianCalendar();
        cal.setTime(baseDate);

        cal.add(Calendar.MILLISECOND, numberOfSec);
        return cal.getTime();
    }

    // -------------------------------------------------------------------------------------------
    public static Date addMonths(Date date, int num) {
        if (date == null)
            return null;

        GregorianCalendar cal = new GregorianCalendar();
        cal.setTime(date);
        cal.add(Calendar.MONTH, num);
        Date dueDate = cal.getTime();

        return dueDate;
    }

    public static Date addYears(Date date, int num) {
        if (date == null)
            return null;

        GregorianCalendar cal = new GregorianCalendar();
        cal.setTime(date);
        cal.add(Calendar.YEAR, num);
        Date dueDate = cal.getTime();

        return dueDate;
    }

    // -------------------------------------------------------------------------------------------
    public static Date setTime(Date baseDate, int hourOfDay, int min, int sec) {
        if (baseDate == null)
            return null;

        Date normalizedDate = DateUtil.normalizeDay(baseDate);

        GregorianCalendar cal = new GregorianCalendar();
        cal.setTime(normalizedDate);

        cal.add(Calendar.HOUR_OF_DAY, hourOfDay);
        cal.add(Calendar.MINUTE, min);
        cal.add(Calendar.SECOND, sec);
        return cal.getTime();
    }

    // -------------------------------------------------------------------------------------------
    public static boolean isAchHoliday(Date date) {
        if (date == null)
            return false;
        Date normalizedDate = DateUtil.normalizeDay(date);

        // for ( ACHHoliday h: ACHHoliday.values() ) {
        // if ( h.getDate().getTime() == normalizedDate.getTime() )
        // return true;
        // }

        return false;
    }

    // -------------------------------------------------------------------------------------------
    public static boolean isExcludedDate(Date date) {
        Calendar calendar = Calendar.getInstance();
        calendar.setTime(date);

//        if ((calendar.get(Calendar.DAY_OF_WEEK) == Calendar.SATURDAY)
//                || (calendar.get(Calendar.DAY_OF_WEEK) == Calendar.SUNDAY) || isAchHoliday(date))
//            return true;

        return false;
    }

    // -------------------------------------------------------------------------------------------
    public static Date normalizeDay(Date date) {
        if (date == null)
            return null;

        GregorianCalendar cal = new GregorianCalendar();
        cal.setTime(date);
        cal.set(Calendar.HOUR_OF_DAY, 0);
        cal.set(Calendar.MINUTE, 0);
        cal.set(Calendar.SECOND, 0);
        cal.set(Calendar.MILLISECOND, 0);

        return cal.getTime();
    }

    // -------------------------------------------------------------------------------------------
    public static Date normalizeMonth(Date date) {
        if (date == null)
            return null;

        GregorianCalendar cal = new GregorianCalendar();
        cal.setTime(date);
        cal.set(Calendar.DAY_OF_MONTH, 1);

        return normalizeDay(cal.getTime());
    }

    // -------------------------------------------------------------------------------------------
    public static Date normalizeYear(Date date) {
        if (date == null)
            return null;

        GregorianCalendar cal = new GregorianCalendar();
        cal.setTime(date);
        cal.set(Calendar.MONTH, Calendar.JANUARY);

        return normalizeMonth(cal.getTime());
    }

    // -------------------------------------------------------------------------------------------
    public static Date getSecMktTradeCutOffTime(Date date) {
        Date normalizedDate = normalizeDay(date);
        GregorianCalendar cal = new GregorianCalendar();
        cal.setTime(normalizedDate);
        cal.set(Calendar.HOUR_OF_DAY, Constants.TRADE_ACCEPTANCE_CUT_OFF_HOUR);

        return cal.getTime();
    }

    public static Date getSecMktNextTradeScheduleDate(Date d) {
        // Holiday or WeekEnd ---
        if (isExcludedDate(d)) {
            return normalizeDay(addBusiessDays(d, 1));
        }

        // Business day ---
        Date cutOffDate = getSecMktTradeCutOffTime(d);
        if (d.before(cutOffDate))
            return d;
        return normalizeDay(addBusiessDays(d, 1));
    }

    // -------------------------------------------------------------------------------------------
    public static Date getLoanIssuingCutOffTime(Date date) {
        Date normalizedDate = normalizeDay(date);
        GregorianCalendar cal = new GregorianCalendar();
        cal.setTime(normalizedDate);
        cal.set(Calendar.HOUR_OF_DAY, Constants.LOAN_ISSUING_CUT_OFF_HOUR);

        return cal.getTime();
    }

    public static Date getLoanIssuingSoftCutOffTime(Date date) {
        long d = getLoanIssuingCutOffTime(date).getTime() - 20 * Timeable.MINUTE;

        Date dd = new Date(d);
        return dd;
    }

    public static Date getNextIssueLoanDate(Date d) {
        // Holiday or WeekEnd ---
        if (isExcludedDate(d)) {
            return normalizeDay(addBusiessDays(d, 1));
        }

        // Business day ---
        Date cutOffDate = getLoanIssuingCutOffTime(d);
        if (d.before(cutOffDate))
            return d;
        return normalizeDay(addBusiessDays(d, 1));
    }

    // -------------------------------------------------------------------------------------------
    public static boolean isSameDay(Date date1, Date date2) {
        if (date1 == null || date2 == null)
            return false;
        return (DateUtil.normalizeDay(date1).getTime() == DateUtil.normalizeDay(date2).getTime());
    }

    // -------------------------
    public static boolean isSameMonth(Date date1, Date date2) {
        if (date1 == null || date2 == null)
            return false;
        return (DateUtil.normalizeMonth(date1).getTime() == DateUtil.normalizeMonth(date2).getTime());
    }

    // -------------------------------------------------------------------------------------------
    public static Date getCutOfDateForStatments() {
        Date jan_2008;
        try {
            jan_2008 = new SimpleDateFormat("yyyy-MM-dd").parse("2008-01-01");
        } catch (ParseException e) {
            e.printStackTrace();
            return null;
        }
        return jan_2008;
    }

    public static long daysBetween(Date startDate, Date endDate) {
        Calendar start = new GregorianCalendar();
        start.setTime(startDate);
        Calendar end = new GregorianCalendar();
        end.setTime(endDate);
        return daysBetween(start, end);
    }

    public static long daysBetween(Calendar startDate, Calendar endDate) {
        Calendar date = (Calendar) startDate.clone();
        long daysBetween = 0;
        while (date.before(endDate)) {
            date.add(Calendar.DAY_OF_MONTH, 1);
            daysBetween++;
        }

        return daysBetween;
    }

    public static long monthsBetween(Date startDate, Date endDate) {
        Calendar start = new GregorianCalendar();
        start.setTime(startDate);
        Calendar end = new GregorianCalendar();
        end.setTime(endDate);
        return monthsBetween(start, end);
    }

    public static long monthsBetween(Calendar startDate, Calendar endDate) {
        Calendar date = (Calendar) startDate.clone();
        long daysBetween = 0;
        while (date.before(endDate)) {
            date.add(Calendar.MONTH, 1);
            daysBetween++;
        }

        return daysBetween;
    }

    public static long yearsBetween(Date startDate, Date endDate) {
        Calendar start = new GregorianCalendar();
        start.setTime(startDate);
        Calendar end = new GregorianCalendar();
        end.setTime(endDate);
        return yearsBetween(start, end);
    }

    public static long yearsBetween(Calendar startDate, Calendar endDate) {
        Calendar date = (Calendar) startDate.clone();
        long yearsBetween = 0;
        date.add(Calendar.YEAR, 1);
        while (date.before(endDate)) {
            date.add(Calendar.YEAR, 1);
            yearsBetween++;
        }

        return yearsBetween;
    }

    public static boolean isBetweenIncl(Date startD, Date endD, Date compareD) {
        if (startD.compareTo(compareD) <= 0 && endD.compareTo(compareD) >= 0)
            return true;
        return false;
    }

    // --------------------------------------------------------------------------------------------------
    public static GregorianCalendar getEndOfThisMonth(Date date) {
        Date todate = DateUtil.normalizeMonth(date);
        todate = DateUtil.addMonths(todate, 1);

        GregorianCalendar toCal = new GregorianCalendar();
        toCal.setTime(todate);
        toCal.add(Calendar.SECOND, -1);

        return toCal;
    }

    // --------------------------------------------------------------------------------------------------
    public static GregorianCalendar getBeginingOfThisMonth(Date date) {
        Date todate = DateUtil.normalizeMonth(date);

        GregorianCalendar toCal = new GregorianCalendar();
        toCal.setTime(todate);

        return toCal;
    }

    // --------------------------------------------------------------------------------------------------
    public static java.util.Date stringtoDate(String dateStr) {
        Date d = null;
        SimpleDateFormat formater = new SimpleDateFormat(FORMAT_ONE);
        try {
            formater.setLenient(false);
            d = formater.parse(dateStr);
        } catch (Exception e) {
            // log.error(e);
            d = null;
        }
        return d;
    }
    // --------------------------------------------------------------------------------------------------

    @Override
    public long getAge() {
        // TODO Auto-generated method stub
        return 0;
    }

    @Override
    public long getTimeLeftBeforeExpiration() {
        // TODO Auto-generated method stub
        return 0;
    }

    @Override
    public boolean isExpired() {
        // TODO Auto-generated method stub
        return false;
    }
}
